package com.itedge.infrastructure.service.domain.impl;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.transaction.annotation.Transactional;

import com.itedge.infrastructure.domain.IEntity;
import com.itedge.infrastructure.service.domain.IEntityService;

/**
 * Abstract JPA IEntityService implementation.
 * 
 * @author jhe
 *
 * @param <E extends IEntity>
 */
public abstract class AbstractJpaEntityService<E extends IEntity> implements IEntityService<E> {

	/**
	 * Override this method in concrete subclasses to provide reference to EntityManager instance.
	 * 
	 * @return EntityManager instance
	 */
	protected abstract EntityManager getEntityManager();
	
	/**
	 * Override this method in concrete subclass to provide Class reference of task data instance.
	 * 
	 * @return Class instance
	 */
	protected abstract Class<E> getClassReference();	
	
	@Override
	@Transactional	
	public void persist(E entity) {
		this.getEntityManager().persist(entity);
	}

	@Override
	@Transactional
	public void remove(E entity) {
        if (this.getEntityManager().contains(entity)) {
            this.getEntityManager().remove(entity);
        } else {
        	E attached = this.getEntityManager().find(this.getClassReference(), entity.getId());
            this.getEntityManager().remove(attached);
        }
	}

	@Override
	@Transactional
	public void flush() {
		this.getEntityManager().flush();
	}

	@Override
	@Transactional
	public E merge(E entity) {
        E merged = this.getEntityManager().merge(entity);
        this.getEntityManager().flush();
        return merged;
	}

	@Override
	public E findEntity(Long id) {
		E result = null;
        if (id == null) return null;
        try {
            result = getEntityManager().find(getClassReference(), id);
        } catch(EmptyResultDataAccessException e){
        	return null;
        } catch(NoResultException e) {
	        return null;
	    }
        return result;
	}
	
	@Override
	public long countEntities() {
		return getEntityManager().createQuery("select count(o) from "+this.getClassReference().getSimpleName()+" o", 
			Long.class).getSingleResult();
	}
	
	@Override
	public List<E> findAllEntities() {
		return getEntityManager().createQuery("select o from "+this.getClassReference().getSimpleName()+" o", 
			this.getClassReference()).getResultList();
	}
	
	@Override
	public List<E> findEntityEntries(int firstResult, int maxResults) {
		return getEntityManager().createQuery("select o from "+this.getClassReference().getSimpleName()+" o", 
			this.getClassReference()).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
	}	

}
